<?php
declare(strict_types=1);

namespace App\Third\Service\Wx;
use App\Common\Constants\ErrorCode;
use App\Common\Constants\Stakeholder;
use App\Common\Service\BaseService;
use App\Order\Service\OrderGoodsService;
use App\Resource\Service\TeamLeaderService;
use App\Third\Event\Refunded;
use App\Third\Event\RefundedPush;
use App\Third\Event\WxRefunded;
use App\Third\Service\Kz\KzMainService;
use Hyperf\Di\Annotation\Inject;
use Psr\EventDispatcher\EventDispatcherInterface;

class WxRefundService extends BaseService
{

    /**
     * @Inject()
     * @var OrderGoodsService
     */
    private $orderGoodsService;

    /**
     * @Inject()
     * @var KzMainService
     */
    private $kzMainService;

    /**
     * @Inject()
     * @var WxPayService
     */
    private $wxPayService;

    /**
     * 吾享用户退款
     * @param array $refundGoodsList
     * @param int $isWhole
     * @param string $kzCusCode
     * @param array $order
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function applyWxRefund(array $refundGoodsList, int $isWhole, string $kzCusCode, array $order)
    {
        $order_no = $refundGoodsList[0]['order_no'];
        $refund_no = $refundGoodsList[0]['refund_no'];
        $shop_id = $refundGoodsList[0]['shop_id'];
        $refundOrderGoodsInfo = [];
        $orderGoodsField = ['id', 'goods_id', 'goods_title', 'status', 'selling_price', 'number', 'refund_price', 'refund_number',
            'shop_refund_number', 'kz_goods_id', 'kz_type_id', 'goods_dis_price','commission','kz_self_num'];
        $orderGoodsList = $this->orderGoodsService->getOrderGoodsList(['order_no' => $order_no], $orderGoodsField);
        if (empty($orderGoodsList)) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单商品信息异常'];
        }
        $refundWxMoney = [];
        foreach ($orderGoodsList as $goods) {
            foreach ($refundGoodsList as $k => $refGoods) {
                if ($refGoods['kz_self_num'] == $goods['kz_self_num']) {
                    array_push($refundWxMoney, bcmul((string)$goods['goods_dis_price'], (string)$refGoods['refund_number'], 2));
                    $refundGoodsList[$k]['kz_goods_id'] = $goods['kz_goods_id'];
                    $refundGoodsList[$k]['kz_type_id'] = $goods['kz_type_id'];
                    $lastNum = $goods['number'] - $goods['shop_refund_number'] - $refGoods['refund_number'];
                    $refundOrderGoodsInfo[] = [
                        'id' => $goods['id'],
                        'refund_price' => $refGoods['refund_price'],
                        'refund_number' => $refGoods['refund_number'],
                        'refund_at' => date('Y-m-d H:i:s', time()),
                        'status' => $lastNum == 0 ? 1 : 0,
                        'is_refund_status' => $lastNum == 0 ? 2 : 1
                    ];
                }
            }
        }
        $orderInfo = [
            'totalPrice' => (float)$order['total_price'],
            'refundWxMoney' => array_sum($refundWxMoney),
            'order_no' => $order_no,
            'refund_no' => $refund_no,
            'shop_id' => $shop_id,
            'order_type' => $order['order_type'],
            'order_source' => $order['order_source'],
            'pay_at' => $order['pay_at'],
            'platform_type' => $order['platform_type'],
            'leader_id' => $order['leader_id'],
            'activity_id' => $order['activity_id'],
            'free_parent_order_no' => $order['free_parent_order_no'],
            'refundCommission' => $order['refundCommission'],
            'refundOrderGoodsInfo' => $refundOrderGoodsInfo
        ];
        switch ($isWhole) {
            case Stakeholder::PART_REFUND:
                return $this->applyWxRefundPart($refundGoodsList, $orderInfo, $kzCusCode);
                break;
            case Stakeholder::WHOLE_REFUND:
                return $this->applyWxRefundWhole($refundGoodsList, $orderInfo, $kzCusCode);
                break;
            default:
        }
    }

    /**
     * 吾享用户部分退
     * @param array $refundGoodsList
     * @param array $orderInfo
     * @param string $kzCusCode
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function applyWxRefundPart(array $refundGoodsList, array $orderInfo, string $kzCusCode)
    {
        $orderInfo['allOrPart'] = 'part';
        // 退款商品列表
        $refundGoodsInfo = $this->refundGoodsList($refundGoodsList, 'part');
        // 客至微信退款
        $kzRes = $this->kzMainService->refund($kzCusCode, $orderInfo['order_no'], (string)$refundGoodsInfo['refundMoney'], $refundGoodsInfo['goodsList']);
        // 吾享微信退款
        $wxRes = $this->wxPayService->comPayCancel($orderInfo['order_no'], $orderInfo['refundWxMoney']);
        if ($wxRes['code'] == 0) return $wxRes;
        // 退款成功, 写入日志
        $this->eventDispatcher->dispatch(new WxRefunded($orderInfo, $kzRes, $wxRes['data']));
        // 外卖退款下单
        $this->eventDispatcher->dispatch(new RefundedPush($orderInfo['order_no'], $refundGoodsInfo['goodsList'], $orderInfo['refundWxMoney'], 1, 1));
        // 更新数据
        $this->eventDispatcher->dispatch(new Refunded($orderInfo, 'user'));
        return ['code' => 1, 'msg' => '退款成功'];
    }

    /**
     * 吾享用户全部退
     * @param array $refundGoodsList
     * @param array $orderInfo
     * @param string $kzCusCode
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function applyWxRefundWhole(array $refundGoodsList, array $orderInfo, string $kzCusCode)
    {
        $orderInfo['allOrPart'] = 'whole';
        // 退款商品列表
        $refundGoodsInfo = $this->refundGoodsList($refundGoodsList, 'whole');
        // 客至微信退款
        $kzRes = $this->kzMainService->refund($kzCusCode, $orderInfo['order_no'], (string)$refundGoodsInfo['refundMoney'], $refundGoodsInfo['goodsList']);
        // 吾享微信退款
        $wxRes = $this->wxPayService->comPayCancel($orderInfo['order_no'], $orderInfo['totalPrice']);
        if ($wxRes['code'] == 0) return $wxRes;
        // 退款成功, 写入日志
        $this->eventDispatcher->dispatch(new WxRefunded($orderInfo, $kzRes, $wxRes['data']));
        // 外卖退款下单
        $this->eventDispatcher->dispatch(new RefundedPush($orderInfo['order_no'], $refundGoodsInfo['goodsList'], $orderInfo['totalPrice'], 0, 0));
        // 更新数据
        $this->eventDispatcher->dispatch(new Refunded($orderInfo, 'user'));
        return ['code' => 1, 'msg' => '退款成功'];
    }

    /**
     * 退款商品列表
     * @param array $refundGoodsList
     * @param string $type
     * @return array
     */
    public function refundGoodsList(array $refundGoodsList, string $type)
    {
        $goodsList = [];
        $refundMoney = bcmul((string)array_sum(array_column($refundGoodsList, 'refund_price')), '100');
        foreach ($refundGoodsList as $goods) {
            $goodsList[] = [
                'fShelfNum' => $goods['kz_self_num'],
                'fDiscount' => 0,
                'goodsId' => $goods['kz_goods_id'] ? $goods['kz_goods_id'] : $goods['goods_id'],
                'goods_id' => $goods['goods_id'],
                'typeId' => $goods['kz_type_id'] ?? 0,
                'goodsName' => $goods['goods_title'],
                'goodsNum' => $goods['refund_number'],
                'goodsPrice' => bcmul((string)$goods['selling_price'], '100'),
                'allPrice' => bcmul((string)$goods['refund_price'], '100')
            ];
        }
        return ['code' => 1, 'refundMoney' => $refundMoney, 'goodsList' => $goodsList];
    }

    /**
     * 门店吾享退款
     * @param array|null $refundGoodsList
     * @param int $isWhole
     * @param string $kzCusCode
     * @param array $orderInfo
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function shopWxRefund(?array $refundGoodsList, int $isWhole, string $kzCusCode, array $orderInfo)
    {
        switch ($isWhole) {
            case Stakeholder::PART_REFUND:
                return $this->shopWxRefundPart($refundGoodsList, $orderInfo, $kzCusCode);
                break;
            case Stakeholder::WHOLE_REFUND:
                return $this->shopWxRefundWhole($orderInfo, $kzCusCode);
                break;
            default:
        }
    }

    /**
     * 吾享门店部分退
     * @param array $refundGoodsList
     * @param array $orderInfo
     * @param string $kzCusCode
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function shopWxRefundPart(array $refundGoodsList, array $orderInfo, string $kzCusCode)
    {
        if ($orderInfo['status'] == Stakeholder::ORDER_PENDING) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单正在申请退款，请先操作退款订单'];
        }
        // 退款商品列表
        $orderGoodsField = ['id', 'goods_id', 'goods_title', 'status', 'selling_price', 'number', 'refund_price', 'refund_number',
            'shop_refund_number', 'kz_goods_id', 'kz_type_id', 'goods_dis_price','commission','kz_self_num'];
        $orderGoodsList = $this->orderGoodsService->getOrderGoodsList(['order_no' => $orderInfo['order_no']], $orderGoodsField);

        if (empty($orderGoodsList)) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单商品信息异常'];
        }
        $refundMoney = [];
        $refundWxMoney = [];
        $refundCommission = [];
        $goodsList = [];
        $orderInfo['updateOrderGoodsData'] = [];
        foreach ($orderGoodsList as $goods) {
            foreach ($refundGoodsList as $refGoods) {
                if ($refGoods['kz_self_num'] == $goods['kz_self_num']) {
                    $trueRefNum = $goods['number'] - $goods['refund_number'] - $goods['shop_refund_number'];
                    if ($goods['number'] == ($goods['refund_number'] + $goods['shop_refund_number']) and $goods['status'] == 1) {
                        return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => $goods['goods_title'] . '已退完,不能重复操作'];
                    }

                    if ($refGoods['refund_num'] > $goods['number']) {
                        return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => $goods['goods_title'] . '退款数量不能大于购买数量'];
                    }

                    if ($refGoods['refund_num'] > $trueRefNum){
                        return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => $goods['goods_title'] . '剩余可退数量' . $trueRefNum];
                    }

                    // 已经部分退且退完的商品
                    if (($trueRefNum == 0) && ($goods['status'] == 1)){
                        continue 2;
                    }
                    // 已经部分退并未退完的商品
                    if (($trueRefNum > 0) && ($goods['status'] == 1)){
                        $refGoods['refund_num'] = $trueRefNum;
                    }

                    $everyRefGoodsAmt = bcmul((string)$goods['selling_price'], (string)$refGoods['refund_num'], 2);
                    array_push($refundMoney, bcmul($everyRefGoodsAmt, '100'));
                    array_push($refundWxMoney, bcmul((string)$goods['goods_dis_price'], (string)$refGoods['refund_num'], 2));
                    array_push($refundCommission, bcmul((string)$goods['commission'], (string)$refGoods['refund_num'], 2));

                    $goodsList[] = [
                        'fShelfNum' => $goods['kz_self_num'],
                        'fDiscount' => 0,
                        'goodsId' => $goods['kz_goods_id'] ? $goods['kz_goods_id'] : $goods['goods_id'],
                        'goods_id' => $goods['goods_id'],
                        'typeId' => $goods['kz_type_id'] ?? 0,
                        'goodsName' => $goods['goods_title'],
                        'goodsNum' => $refGoods['refund_num'],
                        'goodsPrice' => bcmul((string)$goods['selling_price'], '100'),
                        'allPrice' => bcmul($everyRefGoodsAmt, '100')
                    ];
                    $lastNum = $goods['number'] - $goods['refund_number'] - $goods['shop_refund_number'] - $refGoods['refund_num'];
                    $orderInfo['updateOrderGoodsData'][] = [
                        'id' => $goods['id'],
                        'refund_price' => bcadd((string)$goods['refund_price'], $everyRefGoodsAmt, 2),
                        'shop_refund_now' => $refGoods['refund_num'],
                        'shop_refund_number' => $goods['shop_refund_number'] + $refGoods['refund_num'],
                        'refund_at' => date('Y-m-d H:i:s', time()),
                        'status' => $lastNum == 0 ? 1 : 0,
                        'is_refund_status' => $lastNum == 0 ? 2 : 1
                    ];
                }

            }

        }
        $refundTotalNum = array_sum(array_column($goodsList, 'goodsNum'));//退款总数量
        $shopRefundedTotalNumber = array_sum(array_column($orderGoodsList, 'shop_refund_number'));//商家已退款总数量
        $applyRefundedTotalNumber = array_sum(array_column($orderGoodsList, 'refund_number'));//申请已退款总数量
        $orderTotalNumber = array_sum(array_column($orderGoodsList, 'number'));//总数量
        $refundCommission = array_sum($refundCommission);//退佣
        $orderInfo['refundCommission'] = $refundCommission;
        // 检测是否全部退
        if ($orderTotalNumber > ($shopRefundedTotalNumber + $refundTotalNum + $applyRefundedTotalNumber)) {
            $orderInfo['allOrPart'] = 'part';
            $refundMoney = (string)array_sum($refundMoney);
            $refundWxMoney = (string)array_sum($refundWxMoney);
        }
        if ($orderTotalNumber == ($shopRefundedTotalNumber + $refundTotalNum + $applyRefundedTotalNumber)) {
            $orderInfo['allOrPart'] = 'whole';
            $refundMoney = bcadd((string)array_sum($refundMoney), bcmul((string)$orderInfo['freight_price'], '100'));
            $refundWxMoney = bcadd((string)array_sum($refundWxMoney), (string)$orderInfo['freight_price'], 2);
        }
        // 退佣
        if ($refundCommission > 0){
            $this->container->get(TeamLeaderService::class)->backCommission($orderInfo['order_no'], $orderInfo['leader_id'], $refundCommission);
        }
        // 客至微信退款
        $kzRes = $this->kzMainService->refund($kzCusCode, $orderInfo['order_no'], $refundMoney, $goodsList);
        // 吾享微信退款
        $wxRes = $this->wxPayService->comPayCancel($orderInfo['order_no'], $refundWxMoney);
        if ($wxRes['code'] == 0) return $wxRes;
        // 退款成功, 写入日志
        $this->eventDispatcher->dispatch(new WxRefunded($orderInfo, $kzRes, $wxRes['data']));
        // 外卖退款下单
        $this->eventDispatcher->dispatch(new RefundedPush($orderInfo['order_no'], $goodsList, $refundWxMoney, 1, 1));
        // 更新数据
        $this->eventDispatcher->dispatch(new Refunded($orderInfo, 'shop'));
        return ['code' => 1, 'msg' => '退款成功'];
    }

    /**
     * 吾享门店全部退
     * @param array $orderInfo
     * @param string $kzCusCode
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function shopWxRefundWhole(array $orderInfo, string $kzCusCode)
    {
        if ($orderInfo['status'] == Stakeholder::ORDER_PENDING) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单正在申请退款，请先操作退款订单'];
        }
        // 退款商品列表
        $orderGoodsField = ['id', 'goods_id', 'goods_title', 'status', 'selling_price', 'number', 'refund_price',
            'refund_number', 'shop_refund_number', 'kz_goods_id', 'kz_type_id', 'goods_dis_price','commission','kz_self_num'];
        $orderGoodsList = $this->orderGoodsService->getOrderGoodsList(['order_no' => $orderInfo['order_no']], $orderGoodsField);

        if (empty($orderGoodsList)) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单商品信息异常'];
        }

        $shopRefundedTotalNumber = array_sum(array_column($orderGoodsList, 'shop_refund_number'));//已退款总数量
        $orderTotalNumber = array_sum(array_column($orderGoodsList, 'number'));//总数量

        if ($orderTotalNumber == $shopRefundedTotalNumber) {
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => '订单已全部退完,请勿重复操作'];
        }
        $goodsList = [];
        $refundCommission = [];
        $everyRefGoodsAmt = [];
        $everyRefGoodsDisAmt = [];
        $orderInfo['updateOrderGoodsData'] = [];
        foreach ($orderGoodsList as $goods) {
            $trueRefNum = $goods['number'] - $goods['refund_number'] - $goods['shop_refund_number'];
            // 已经部分退且退完的商品
            if (($trueRefNum == 0) && ($goods['status'] == 1)){
                continue;
            }
            // 已经部分退并未退完的商品
            if (($trueRefNum > 0) && ($goods['status'] == 1)){
                $goods['number'] = $trueRefNum;
            }
            $refGoodsAmt = bcmul((string)$goods['selling_price'], (string)$goods['number'], 2);
            $refundDis = bcmul((string)$goods['goods_dis_price'], (string)$goods['number'], 2);
            $goodsList[] = [
                'fShelfNum' => $goods['kz_self_num'],
                'fDiscount' => 0,
                'goodsId' => $goods['kz_goods_id'] ? $goods['kz_goods_id'] : $goods['goods_id'],
                'goods_id' => $goods['goods_id'],
                'typeId' => $goods['kz_type_id'] ?? 0,
                'goodsName' => $goods['goods_title'],
                'goodsNum' => $goods['number'],
                'goodsPrice' => bcmul((string)$goods['selling_price'], '100'),
                'allPrice' => bcmul($refGoodsAmt, '100')
            ];
            $orderInfo['updateOrderGoodsData'][] = [
                'id' => $goods['id'],
                'refund_price' => $refGoodsAmt,
                'shop_refund_now' => $goods['number'],
                'shop_refund_number' => $goods['number'] + $goods['shop_refund_number'],
                'refund_at' => date('Y-m-d H:i:s', time()),
                'status' => 1,
                'is_refund_status' => 2
            ];
            array_push($everyRefGoodsAmt, $refGoodsAmt);
            array_push($everyRefGoodsDisAmt, $refundDis);
            array_push($refundCommission, bcmul((string)$goods['commission'], (string)$goods['number'], 2));
        }
        $refGoodsTotalAmt = bcadd((string)array_sum($everyRefGoodsAmt), (string)$orderInfo['freight_price'], 2);// 加运费
        $refGoodsDisTotalAmt = bcadd((string)array_sum($everyRefGoodsDisAmt), (string)$orderInfo['freight_price'], 2);// 加运费
        $refundMoney = bcmul($refGoodsTotalAmt, '100');
        $refundCommission = array_sum($refundCommission);
        $orderInfo['allOrPart'] = 'whole';
        $orderInfo['refundCommission'] = $refundCommission;
        // 退佣
        if ($refundCommission > 0){
            $this->container->get(TeamLeaderService::class)->backCommission($orderInfo['order_no'], $orderInfo['leader_id'], $refundCommission);
        }
        // 客至微信退款
        $kzRes = $this->kzMainService->refund($kzCusCode, $orderInfo['order_no'], (string)$refundMoney, $goodsList);
        // 吾享微信退款
        $wxRes = $this->wxPayService->comPayCancel($orderInfo['order_no'], $refGoodsDisTotalAmt);
        if ($wxRes['code'] == 0) return $wxRes;
        // 退款成功, 写入日志
        $this->eventDispatcher->dispatch(new WxRefunded($orderInfo, $kzRes, $wxRes['data']));
        // 外卖退款下单
        $this->eventDispatcher->dispatch(new RefundedPush($orderInfo['order_no'], $goodsList, $refGoodsDisTotalAmt, 0, 0));
        // 更新数据
        $this->eventDispatcher->dispatch(new Refunded($orderInfo, 'shop'));
        return ['code' => 1, 'msg' => '退款成功'];
    }

}
